import {Routes} from '@angular/router';
/**
 * 深度比较
 */

export class ToolClass {

    static compDeepValue(a: any, b: any, noProp?: Array<string>): Boolean {
        if (a && b) {
            let typeA = Object.prototype.toString.call(a);
            let typeB = Object.prototype.toString.call(b);
            if (typeA == "[object Object]" && typeB == "[object Object]") {
                let keyA = Object.keys(a).filter(i => !noProp.includes(i));
                let keyB = Object.keys(b).filter(i => !noProp.includes(i));
                if (keyA.length != keyB.length) {
                    return false
                }
                for (let i of keyA) {
                    if (!this.compDeepValue(a[i], b[i], noProp)) {
                        return false
                    }
                }
                return true;
            } else if (
                typeA == "[object Array]" &&
                typeB == "[object Array]"
            ) {
                if (a.length != b.length) {
                    // console.log(a, b, '数组长度')
                    return false
                }
                for (let i = 0; i < a.length; i++) {
                    if (!this.compDeepValue(a[i], b[i], noProp)) {
                        return false
                    }
                }
                return true;
            } else {
                if (a != b) {
                    return false
                }
                return a == b;
            }
        } else if (!a && !b) {
            return true;
        }
        return a == b;
    }

    static numToFixed(data: string | number, num: number = 3): number {
        let a = Number(data);
        if (Number.isNaN(a)) {
            return 0
        }
        return Number(a.toFixed(num))
    }
}

export function s4() {
    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}


const strHtml = {
    '<': '&lt;',
    '>': '&gt;'
}

export function htmlToStr(str: string){
    return str?.replace(/<|>/g, m => strHtml[m]) || ''
}

export function listToTree(
    dataList: any[],
    cfgProp: {idProp: string; parentIdProp: string; childProp: string}
){
    let res = [];
    let data = {};
    let maxIndex = 0;
    if(!dataList?.length){
        return {data: res, maxIndex: -1}
    }
    for (let i = dataList.length - 1; i >= 0; i--) {
        let item = dataList[i];
        item[cfgProp.childProp] = [];
        item._level_ = 0;
        item._expand_ = true;
        data[item[cfgProp.idProp]] = { obj: item, zIndex: 0 };
        if (!item[cfgProp.parentIdProp]) {
            dataList.splice(i, 1);
            res.unshift(item);
        }
    }
    for (let i = 0; i < dataList.length; i++) {
        let item = dataList[i];
        let currParent = data[item[cfgProp.parentIdProp]];
        let parent = currParent;
        while (parent){
            item._level_++;
            parent = data[parent.obj[cfgProp.parentIdProp]];
            if(parent && parent.obj._level_ != 0){
                item._level_ += parent.obj._level_ + 1;
                break
            }
        }
        maxIndex = Math.max(maxIndex, item._level_);
        if(currParent){
            currParent.obj[cfgProp.childProp].push(item);
        }
    }
    return {data: res, maxIndex}
}

export function listToTreeWidthParent(dataList: any[], option: {idProp: string; parentIdProp: string; childrenProp: string}) {
    let res = [];
    let data = {};
    let deep = 0;
    if (!dataList?.length) {
        return { data: res, deep: -1 };
    }
    for (let i = dataList.length - 1; i >= 0; i--) {
        let item = dataList[i];
        item[option.childrenProp] = [];
        item._level_ = 0;
        item._expand_ = true;
        data[item[option.idProp]] = { obj: item, zIndex: 0 };
        if (!item[option.parentIdProp]) {
            dataList.splice(i, 1);
            res.unshift(item);
        }
    }
    for (let i = 0; i < dataList.length; i++) {
        let item = dataList[i];
        let currParent = data[item[option.parentIdProp]];
        let parent = currParent;
        while (parent) {
            item._level_++;
            parent = data[parent.obj[option.parentIdProp]];
            if (parent && parent.obj._level_ != 0) {
                item._level_ += parent.obj._level_ + 1;
                break;
            }
        }
        deep = Math.max(deep, item._level_);
        if (currParent) {
            currParent.obj[option.childrenProp].push(item);
            item._parent = currParent.obj;
        }
    }
    return { data: res, deep };
}

export function toTreeNodeOptions(r: any[], checkParent: boolean = false, cfgProp: {
    idProp: string;
    parentIdProp: string;
    childProp: string;
    titleProp: string;
    keyProp?: string
} = {
    idProp: 'Guid',
    parentIdProp: 'ParentGuid',
    childProp: 'Children',
    titleProp: 'Text'
}){
    r.forEach(i => {
        i.children = getDeepObj(i, cfgProp.childProp);
        i.key = i[cfgProp.keyProp || cfgProp.idProp];
        i.title = i[cfgProp.titleProp];
        i.selectable = true;
        if(i.children && i.children.length !== 0){
            i.expanded = true;
            i.selectable = checkParent;
        } else {
            i.isLeaf = true
        }
        if(i.children && i.children.length !== 0){
            toTreeNodeOptions(i.children, checkParent, cfgProp)
        }
    })

}

function routerToObj(data: Routes, res: {path: string, data: {title?: string, [prop: string]: any}}[]){
    if(Array.isArray(data)){
        data.forEach(i => {
            if(i.data){
                let data = {
                    path: i.path,
                    data: i.data,
                    children: []
                };
                res.push(data);
                routerToObj(i.children, data.children)
            }
        })
    }
}

export function getRouterData(data: Routes): {path: string, data: {title: string, [prop: string]: any}}[]{
     let res = [];
    routerToObj(data, res);
    return res
}

export function getTreeNodeById<T>(id: any, data: T[], option?:{id?: string; children?: string} ): {
    zIndexArr: {d: T; i: number}[],
    data: T,
} {
    let prop =  {
        id: 'id',
        children: 'children'
    }
    Object.assign(prop, option || {})
    let s = {
        zIndexArr: [],
        data: null
    };
    let findNode = (data: T[]) => {
        if(!Array.isArray(data)) return false;
        for (let i = 0; i < data.length; i++) {
            let d = data[i];
            s.zIndexArr.push({
                d,
                i
            });
            if(d[prop.id] === id){
                s.data = d
                return true
            }
            if(findNode(getDeepObj(d, prop.children))){
                return true
            }
            s.zIndexArr.pop()
        }
        return false
    }
    findNode(data);
    return s
}

interface optionLeafPropCfg {field?: string; children?: string, addSearch?: string}

export function getOptionLeaf(arr, excludeProp: string[] = [], propCfg: optionLeafPropCfg = {}) {
    let props:optionLeafPropCfg = Object.assign({field: 'Field', children: 'children', addSearch: 'addSearch'}, propCfg || {});
    let res = [];
    const getChild = function(){
        for (let i = 0; i < arr.length; i++) {
            let d = arr[i];
            if (d[props.addSearch] && !excludeProp.includes(d[props.field])) {
                res.push(d);
            }
            if (d[props.children]) {
                this.getChild(d[props.children])
            }
        }
    }
    getChild();
    return res;
}


export function  dateformat(value: Date, format: string= 'yyyy-MM-dd'): string {
    if (value) {
        value = new Date(value);
        const o = {
            'M+': value.getMonth() + 1,     // 月份
            'd+': value.getDate(),     // 日
            'h+': value.getHours(),     // 小时
            'm+': value.getMinutes(),     // 分
            's+': value.getSeconds(),     // 秒
            'q+': Math.floor((value.getMonth() + 3) / 3),  // 季度
            S: value.getMilliseconds()    // 毫秒
        };
        if (/(y+)/.test(format)) {
            format = format.replace(RegExp.$1, (value.getFullYear() + '').substr(4 - RegExp.$1.length));
        }
        for (const k in o) {
            if (new RegExp('(' + k + ')').test(format)) {
                format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
            }
        }
        return format;
    }
    return ''
}

export function format(Str: string, objdata: object) {
    let res = Str;
    if (res.length == 0) return this;
    for (var key in objdata) {
        res = res.replace(new RegExp("\\{" + key + "\\}", "g"), objdata[key]);
    }
    return res;
}

/*
保留数字小数位数
*/
export function dealNumber(num: number | string, size: number = 4, t: 'multiply' | 'divide' ='divide'){
    num = Number(num);
    if(!num) return 0;
    let m = size;
    let c = num.toString().split('.')[1];
    if(t === 'divide'){
        if(c) m += c.length;
        return (num / (10 ** size)).toFixed(m)
    }
    if(c) {
        m = c.length - m;
        m = m < 0 ? 0 : m;
    }
    return (num * (10 ** size)).toFixed(m)
}


/**
 * 两个对象可检测引用数据类型Array、Object进行深度值比较
 * @param {any} a
 * @param {any} b
 * @param {string []} noProp
 * @returns {Boolean} 不等于返回false，相等返回true
 *
 */
export function isEquality(a: any, b: any, ...noProp: string[]): boolean {
    if (a && b) {
        let typeA = Object.prototype.toString.call(a)
        let typeB = Object.prototype.toString.call(b)
        if (typeA === typeB) {
            switch (typeA) {
                case '[object Object]':
                    let keyA = Object.keys(a).filter((i) => !noProp.includes(i))
                    let keyB = Object.keys(b).filter((i) => !noProp.includes(i))
                    if (keyA.length != keyB.length) {
                        return false
                    }
                    for (let i of keyA) {
                        if (!isEquality(a[i], b[i], ...noProp)) {
                            return false
                        }
                    }
                    return true
                case '[object Array]':
                    if (a.length !== b.length) {
                        return false
                    }
                    for (let i = 0; i < a.length; i++) {
                        if (!isEquality(a[i], b[i], ...noProp)) {
                            return false
                        }
                    }
                    return true
                case "[object String]":
                    try {
                        let aC = JSON.parse(a);
                        let bC = JSON.parse(b);
                        return isEquality(aC, bC, ...noProp)
                    } catch (e) {
                        return a == b
                    }
                default:
                    return a == b
            }
        }
        return false
    }
    return a == b;
}

export function delTreeNoneNode(ids: Set<string>, tree: any[], ...cfg: string[]){
    let stu = false;
    if(Array.isArray(tree) && tree.length){
        for (let i = tree.length - 1; i >=0; i--) {
            let data = tree[i];
            let childHas = delTreeNoneNode.apply(null, [ids, data[cfg[1] || 'children'], ...cfg]);
            if(!childHas && !ids.has(data[cfg[0] || 'guid'])){
                tree.splice(i, 1);
            } else {
                stu = true;
            }
        }
    }
    return stu
}

export function treeToMap<T extends Object>(data: T[], id: string, child: string = 'children'): {[prop: string]: T}{
    let r = {};
    let dealFunc = (d: any[]) => {
        if(Array.isArray(d)){
            d.forEach(i => {
                r[i[id]] = i;
                dealFunc(getDeepObj(i, child));
            })
        }
    }
    dealFunc(data);
    return r
}

export function getDeepObj(data: any, d: string, split = '.'){
    let dArr = d.split(split);
    let r = data;
    for(let i = 0; i < dArr.length; i++){
        r = r[dArr[i]]
        if(!r){
            return r
        }
    }
    return r
}


function setDefineProp(obj: any,  name: string, value: any){
    Object.defineProperty(obj, name, {
        writable: false,
        value,
        enumerable: false,
        configurable: false
    })
}

export function verifyDo<T>(
    sourceArr: T[],
    verifyFunc: {name: string; verifyF: (data: T) => boolean}[]
): { len: number; verify: boolean[] } {
    let verifyB = Array.from({length: verifyFunc.length}).map(i => false);
    const maxArr = sourceArr.map(item => {
        let max = 0;
        verifyFunc.forEach((it, ij) => {
            let r = !!it.verifyF(item);
            verifyB[ij] = verifyB[ij] || r;
            max += Number(r);
            setDefineProp(item, it.name, r);
        })
        return max
    })
    if(maxArr.length){
        return {len: Math.max.apply(null, maxArr), verify: verifyB}
    }
    return {len: 0, verify: verifyB}
}


export function trimObjectDeep(data: any){
    let v = data;
    if(v){
        const t = Object.prototype.toString.call(v);
        switch (t) {
            case '[object Array]':
                return v.map(i => trimObjectDeep(i))
            case '[object Object]':
                Object.keys(v).forEach(i => {
                    v[i] = trimObjectDeep(v[i])
                })
                return v
            case '[object String]':
                try {
                    let obj = JSON.parse(v);
                    return JSON.stringify(trimObjectDeep(obj))
                } catch (e) {
                    return v.trim()
                }
        }
        return v
    }
    return v
}

export function treeSortDeep<T>(data: T[], sortFun?: (a: T, b: T) => number, id: string = 'id', child: string = 'children'): T[]{
    if(Array.isArray(data)){
        data.forEach(i => {
            if(Array.isArray(i[child])){
                i[child] = treeSortDeep(i[child], sortFun, id, child);
            }
        })
        return data.sort((a, b) => {
            if(sortFun){
                return sortFun(a, b)
            }
            return a[id] - b[id]
        })
    }
    return data

}

export function treeSizeDeep<T>(data: T[], child: string = 'children'): number{
    let l = 0;
    if(Array.isArray(data)){
        data.forEach(i => {
            l += treeSizeDeep(i[child])
        })
        l += data.length;
    }
    return l
}


export function toNzSelectOption(data: any[]): any[] {
    if (!data) {
        return [];
    }
    return data.map(item => ({
        value: item.Value,
        label: item.Label
    }));
}
